package com.clei.Y2023.M10.D13.com;

import com.clei.Y2023.M10.D13.packet.ComPacketHandler;
import com.clei.Y2023.M10.D13.packet.PacketHandler;
import com.clei.Y2023.M10.D13.packet.TestPacketHandler;
import com.clei.utils.PrintUtil;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;

import java.util.Enumeration;
import java.util.TooManyListenersException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 应用入口
 *
 * @author Y
 * @date 2023-10-21
 */
public class Application {

    /**
     * 串口名
     */
    private final String serialPortName;

    /**
     * 开启测试
     */
    private final boolean testEnabled;

    /**
     * 串口
     */
    private SerialPort serialPort;

    /**
     * 阻塞队列 存放数据
     */
    private final BlockingQueue<String> msgQueue = new LinkedBlockingQueue<>();

    public Application(String serialPortName) {
        this(serialPortName, false);
    }

    public Application(String serialPortName, boolean testEnabled) {
        this.serialPortName = serialPortName;
        this.testEnabled = testEnabled;
    }

    /**
     * 初始化
     *
     * @return 初始化结果
     */
    private int init() {
        // 通过串口通信管理类获得当前连接上的端口列表
        //（获取一个枚举对象，该 CommPortIdentifier 对象包含系统中每个端口的对象集[串口、并口]）
        Enumeration<?> portList = CommPortIdentifier.getPortIdentifiers();
        while (portList.hasMoreElements()) {
            // 获取相应串口对象
            CommPortIdentifier portManager = (CommPortIdentifier) portList.nextElement();
            /*
             *  判断端口类型是否为串口
             *  PORT_SERIAL = 1; 【串口】
             *  PORT_PARALLEL = 2; 【并口】
             *  PORT_I2C = 3; 【I2C】
             *  PORT_RS485 = 4; 【RS485】
             *  PORT_RAW = 5; 【RAW】
             */
            if (portManager.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                PrintUtil.log("串口设备名称 : {}", portManager.getName());
                // 判断模拟 COM1 串口存在，就打开该串口
                if (!this.serialPortName.equals(portManager.getName())) {
                    continue;
                }
                PrintUtil.log("测试串口设备名称 : {}", this.serialPortName);
                try {
                    if (this.serialPort == null) {
                        // 打开串口，设置名字为COM_1(自定义),延迟阻塞时等待3000毫秒（赋值给预设的串口引用）
                        this.serialPort = (SerialPort) portManager.open(this.serialPortName, 3000);
                        PrintUtil.log("串口设备已打开");
                    }
                } catch (PortInUseException e) {
                    PrintUtil.log("串口使用异常 ", e);
                    return 0;
                }
                // 在串口引用不为空时进行下述操作
                if (this.serialPort != null) {
                    // 1. 设置串口监听器
                    try {
                        PacketHandler packetHandler = this.testEnabled ? new TestPacketHandler() : new ComPacketHandler();
                        ComMessageListener messageListener = new ComMessageListener(this.serialPort, this.msgQueue, packetHandler);
                        serialPort.addEventListener(messageListener);
                    } catch (TooManyListenersException e) {
                        PrintUtil.log("串口监听器添加异常 ", e);
                        return 0;
                    }
                    // 设置监听器在有数据时通知生效
                    serialPort.notifyOnDataAvailable(true);

                    // 2. 设置串口相关读写参数
                    try {
                        // 比特率、数据位、停止位、校验位
                        serialPort.setSerialPortParams(9600,
                                SerialPort.DATABITS_8,
                                SerialPort.STOPBITS_1,
                                SerialPort.PARITY_NONE);
                    } catch (UnsupportedCommOperationException e) {
                        PrintUtil.log("串口设置操作异常 ", e);
                        return 0;
                    }
                    return 1;
                }
                return 0;
            }
        }
        PrintUtil.log("串口 {} 不存在", this.serialPortName);
        return 0;
    }

    /**
     * 启动
     */
    private void run() {
        ComMessageHandler comMessageHandler = new ComMessageHandler(this.msgQueue, this.testEnabled);
        new Thread(comMessageHandler).start();
    }

    /**
     * 启动
     */
    public void start() {
        int i = this.init();
        if (i == 1) {
            // 线程启动
            this.run();
        } else {
            PrintUtil.log("启动失败");
        }
    }
}

